package fi.hut.soberit.agilefant.reporting.security;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.ws.rs.core.MultivaluedMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.util.MultivaluedMapImpl;

import fi.hut.soberit.agilefant.model.export.User;
import fi.hut.soberit.agilefant.reporting.communication.RemoteCommunicationBuilder;

public class RemoteAuthenticationProviderImpl implements AuthenticationProvider {

	@Autowired
	private RemoteCommunicationBuilder remoteCommunicationBuilder;
	
	public Authentication authenticate(Authentication auth)
			throws AuthenticationException {
		String user = (String) auth.getPrincipal();
		String passwd = (String) auth.getCredentials();
		Client c = remoteCommunicationBuilder.createClient();
		String encoded;
		try {
			encoded = encodePassword(passwd);
		} catch (NoSuchAlgorithmException e1) {
			return null;
		}
		WebResource r = c.resource(this.remoteCommunicationBuilder.getBaseURI() + "/authenticate");
		MultivaluedMap<String, String> m = new MultivaluedMapImpl();
		m.add("username", user);
		m.add("password", encoded);
		User agilefantUser = null;
		try {
			agilefantUser = r.post(User.class, m);
		}
		catch (UniformInterfaceException uie) {
		  int status = uie.getResponse().getStatus();
		  System.out.println(uie.getMessage());
		  if (status == 401) {
		    // HTTP authentication to Agilefant's remote API failed
		    throw new AuthenticationServiceException("Can't connect to Agilefant's remote API. Try checking the remote.properties file.", uie);
		  } else if (status == 403 || status == 412) {
		    // Agilefant authentication failed
		    throw new BadCredentialsException("Bad username or password");
		  } else {
		    throw new AuthenticationServiceException("Unknown error occurred when authenticating", uie);
		  }
		} catch (Exception e) {
		  throw new AuthenticationServiceException("Unknown error occurred", e);
		}
		return new AgilefantAuthenticationToken(user, passwd, agilefantUser);
	}

	private String encodePassword(String passwd)
			throws NoSuchAlgorithmException {
		MessageDigest enc = null;
		enc = MessageDigest.getInstance("MD5");

		enc.update(passwd.getBytes(), 0, passwd.length());
		String encoded = new BigInteger(1, enc.digest()).toString(16);
		return encoded;
	}

	public boolean supports(Class<?> arg0) {
		return true;
	}
}
